﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfAppLegoNXTCommSample
{
    using System.IO.Ports;
    using ETRC.CommLib;

    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            UpdateCOMPortComboBox();
        }
#if COMMUNICATION_MANAGER
        private CommunicationManager myCommMgr;
#else
        private IDataExchangeManager myCommMgr;
        System.Windows.Threading.DispatcherTimer getReceiveDataTimer;
#endif
        private bool dataEncodingHex = false;

        private void buttonConnect_Click(object sender, RoutedEventArgs e)
        {
            if (buttonConnect.Content.ToString().CompareTo("Connect") == 0)
            {
#if COMMUNICATION_MANAGER
                myCommMgr = new CommunicationManager();
#else
//                myCommMgr = new DataExchangeManager();
                myCommMgr = new DXComPortManager();
#endif
                SetPortParams();

                try
                {
                    myCommMgr.CurrentPort.Open();

                    if (myCommMgr.CurrentPort.IsOpen)
                    {
                        tbStatus.Text = "Connected";
                        buttonSend.IsEnabled = true;
                        dataEncodingHex = false;
                        if (((ComboBoxItem)cbEncoding.SelectedItem).Content.ToString().CompareTo("HEX") == 0)
                        {
                            dataEncodingHex = true;
                        }
#if COMMUNICATION_MANAGER
                    myCommMgr.CurrentPort.DataReceived += new SerialDataReceivedEventHandler(CurrentPort_DataReceived);
#else
                        myCommMgr.Start();
                        getReceiveDataTimer = new System.Windows.Threading.DispatcherTimer();
                        getReceiveDataTimer.Tick += new EventHandler(timer_Tick);
                        getReceiveDataTimer.Interval = new TimeSpan(0, 0, 1);
                        getReceiveDataTimer.Start();
#endif
                        buttonConnect.Content = "Close";
                    }
                    else
                    {
                        tbStatus.Text = "Connection Failed";
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Port Open Failed : " + ex.Message);
                    tbStatus.Text = "Connection Exception";
                }
            }
            else
            {
#if COMMUNICATION_MANAGER
                myCommMgr.CurrentPort.Close();
#else
                getReceiveDataTimer.Stop();
                myCommMgr.Stop();
#endif
                tbStatus.Text = "Unconnected";
                buttonConnect.Content = "Connect";
                buttonSend.IsEnabled = false;
            }
        }

        private void SetPortParams()
        {
            string portName = cbCOMPorts.SelectedItem.ToString();
            int baudRate = int.Parse(tbBaudRate.Text);
            myCommMgr.CurrentPort.BaudRate = baudRate;

            int dataBit = int.Parse(tbDataBit.Text);
            Parity parity = Parity.None;
            switch (((ComboBoxItem)this.cbParity.SelectedItem).ToString())
            {
                case "Even":
                    parity = Parity.Even;
                    break;
                case "Mark":
                    parity = Parity.Mark;
                    break;
                case "Odd":
                    parity = Parity.Odd;
                    break;
                case "Space":
                    parity = Parity.Space;
                    break;
            }
            myCommMgr.CurrentPort.Parity = parity;
            StopBits stopBits = StopBits.One;
            switch (((ComboBoxItem)cbStopBit.SelectedItem).Content.ToString())
            {
                case "None":
                    stopBits = StopBits.None;
                    break;
                case "OnePointFive":
                    stopBits = StopBits.OnePointFive;
                    break;
                case "Two":
                    stopBits = StopBits.Two;
                    break;
            }
            myCommMgr.CurrentPort.StopBits = stopBits;
            Handshake handshake = Handshake.None;
            switch (((ComboBoxItem)cbFlowControl.SelectedItem).Content.ToString())
            {
                case "RequestToSend":
                    handshake = Handshake.RequestToSend;
                    break;
                case "RequestToSendXOnAndXOff":
                    handshake = Handshake.RequestToSendXOnXOff;
                    break;
                case "XOnAndXOff":
                    handshake = Handshake.XOnXOff;
                    break;
            }
            myCommMgr.CurrentPort.Handshake = handshake;
            myCommMgr.CurrentPort.PortName = portName;
        }

#if COMMUNICATION_MANAGER
        void CurrentPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (myCommMgr.CurrentPort.IsOpen)
            {
                this.Dispatcher.BeginInvoke(new SerialDataReceivedEventHandler(DataReceived), new object[] { sender, e });
            }
        }

        void DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (myCommMgr.CurrentPort.IsOpen)
            {
                if (e.EventType == SerialData.Chars)
                {
                    int bytesToRead = myCommMgr.CurrentPort.BytesToRead;
                    byte[] buffer = new byte[bytesToRead];
                    int readBytes = 0;
                    while (readBytes < bytesToRead)
                    {
                        int length = myCommMgr.CurrentPort.Read(buffer, readBytes, bytesToRead - readBytes);
                        readBytes += length;
                    }
                    WriteReadHistory(buffer);
                }
            }
        }
#else
        void timer_Tick(object sender, EventArgs e)
        {
            if (cbShowRecivedDataRT.IsChecked.Value)
            {
                byte[] readData = null;
                while ((readData = myCommMgr.GetReceiveData()) != null)
                {
                    WriteReadHistory(readData);
                }
            }
        }
#endif

        private void WriteReadHistory(byte[] buffer)
        {
            StringBuilder sb = new StringBuilder();
            System.IO.TextWriter writer = new System.IO.StringWriter(sb);
            DateTime nowTime = DateTime.Now;
            writer.WriteLine(String.Format("{0}:{1}:{2}.{3} -", nowTime.Hour, nowTime.Minute, nowTime.Second, nowTime.Millisecond));
            if (dataEncodingHex)
            {
                writer.WriteLine(ConvertByteToHexString(buffer));
            }
            else
            {
                writer.WriteLine(System.Text.Encoding.ASCII.GetString(buffer));
            }
            rtbReceivedData.AppendText(writer.ToString());
        }

        private string ConvertByteToHexString(byte[] buf)
        {
            StringBuilder sb = new StringBuilder();
            System.IO.TextWriter writer = new System.IO.StringWriter(sb);
            foreach (var b in buf)
            {
                int data = b;
                writer.Write(Convert.ToString(data, 16).ToUpper());
            }
            return sb.ToString();
        }

        private void buttonSend_Click(object sender, RoutedEventArgs e)
        {
            byte[] sendData;
            if (dataEncodingHex)
            {
                sendData = ConvertHexStringToByte(tbSendData.Text);
            }
            else
            {
                sendData = System.Text.Encoding.ASCII.GetBytes(tbSendData.Text);
            }

#if COMMUNICATION_MANAGER
            myCommMgr.SendDataAsync(sendData, 0, sendData.Length);
#else
            myCommMgr.SendData(sendData, 0, sendData.Length);
#endif

            rtbSendDataHistory.AppendText(tbSendData.Text);
        }

        private byte[] ConvertHexStringToByte(string data)
        {
            byte[] buf = new byte[data.Length / 2];
            for (int i = 0; i < data.Length / 2; i++)
            {
                string d = data.Substring(2 * i, 2);
                buf[i] = Convert.ToByte(d, 16);
            }
            return buf;
        }

        private void buttonCOMPortReflesh_Click(object sender, RoutedEventArgs e)
        {
            UpdateCOMPortComboBox();
        }

        private void UpdateCOMPortComboBox()
        {
            cbCOMPorts.Items.Clear();
            foreach (var port in SerialPort.GetPortNames())
            {
                cbCOMPorts.Items.Add(port);
            }
        }

        private void buttonETRoboConSetting_Click(object sender, RoutedEventArgs e)
        {
            tbBaudRate.Text = "9600";
            tbDataBit.Text = "8";
            cbParity.SelectedIndex = 2;
            cbStopBit.SelectedIndex = 1;
            cbFlowControl.SelectedIndex = 0;

            buttonETRoboConDrive.IsEnabled = true;
            buttonETRoboConStop.IsEnabled = true;
        }

        private void buttonETRoboConDrive_Click(object sender, RoutedEventArgs e)
        {
            if (myCommMgr != null && myCommMgr.CurrentPort.IsOpen)
            {
                MessageBox.Show("ポートをオープンしてください");
            }
            tbETRoboConDriveStatus.Text="To Send 'DRIVE(1)'";
            var resolver = new ETRoboConBasicResolver();
            byte[] command = new byte[1];
            command[0] = 0x31;
            resolver.CurrentCommand = command[0];
            var response = new List<byte[]>();
            myCommMgr.SendSyncData(command, 0, 1, response, resolver);
            tbETRoboConDriveStatus.Text = "Done.";
        }

        private void buttonETRoboConStop_Click(object sender, RoutedEventArgs e)
        {
            if (myCommMgr != null && myCommMgr.CurrentPort.IsOpen)
            {
                MessageBox.Show("ポートをオープンしてください");
            }
            tbETRoboConStopStatus.Text = "To Send 'STOP(0)'";
            byte[] command = new byte[1];
            command[0] = 0x30;
            var resolver = new ETRoboConBasicResolver();
            resolver.CurrentCommand = command[0];
            var response = new List<byte[]>();
            myCommMgr.SendSyncData(command, 0, 1, response, resolver);
            tbETRoboConStopStatus.Text = "Done.";
        }

        private void buttonClearReceivedData_Click(object sender, RoutedEventArgs e)
        {
            
        }
    }

    class ETRoboConBasicResolver : ResponseDataResolver
    {
        public byte CurrentCommand { get; set; }
        public void IsValidResponse(byte[] response, out bool isResponse, out bool isDone)
        {
            isResponse = false;
            isDone = false;
            if (response.Length == 1 && response[0] == CurrentCommand)
            {
                isResponse = true;
                isDone = true;
            }
        }
    }
}
